/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pt.utl.ist.isr.livetrack.ui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(dtd = "-//pt.utl.ist.isr.livetrack.ui//MultiCameraView//EN",
autostore = false)
@TopComponent.Description(preferredID = "MultiCameraViewTopComponent",
//iconBase="SET/PATH/TO/ICON/HERE", 
persistenceType = TopComponent.PERSISTENCE_ALWAYS)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
@ActionID(category = "Window", id = "pt.utl.ist.isr.livetrack.ui.MultiCameraViewTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(displayName = "#CTL_MultiCameraViewAction",
preferredID = "MultiCameraViewTopComponent")
public final class MultiCameraViewTopComponent extends TopComponent implements DropTargetListener {

    private static final int MARGIN = 10;
//
//    Action[] actions = new Action[]{
//        ActionFactory.createAcceptAction(new AcceptProvider() {
//
//    public ConnectorState isAcceptable(Widget widget, Point point, Transferable transferable) {
//        Image dragImage = getImageFromTransferable(transferable);
//        JComponent view = getView();
//        Graphics2D g2 = (Graphics2D) view.getGraphics();
//        Rectangle visRect = view.getVisibleRect();
//        view.paintImmediately(visRect.x, visRect.y, visRect.width, visRect.height);
//        g2.drawImage(dragImage,
//                AffineTransform.getTranslateInstance(point.getLocation().getX(),
//                point.getLocation().getY()),
//                null);
//        return ConnectorState.ACCEPT;
//    }
//
//    public void accept(Widget widget, Point point, Transferable transferable) {
//        Image image = getImageFromTransferable(transferable);
//        Widget w = GraphSceneImpl.this.addNode(new MyNode(image));
//        w.setPreferredLocation(widget.convertLocalToScene(point));
//    }
//})
//    };
    private ArrayList<CameraNode> cameras = new ArrayList<CameraNode>();
    private ArrayList<CameraView> views = new ArrayList<CameraView>();
    private final Object updateLock = new Object();
    private final CameraViewController controller = new CameraViewController();

    private void update() {
        synchronized (updateLock) {
            camerasPanel.removeAll();
            for (CameraView cameraView : views) {
                controller.getObservers().remove(cameraView);
                cameraView.dispose();
            }
            views.clear();
            GridLayout layout = (GridLayout) camerasPanel.getLayout();
            camerasPanel.setLayout(layout);
            layout.setColumns((Integer) nColsSpinner.getValue());
            layout.setRows(0);

            Collections.sort(cameras, new Comparator<CameraNode>() {

                @Override
                public int compare(CameraNode o1, CameraNode o2) {
                    return o1.getCamera().getId().compareTo(o2.getCamera().getId());
                }
            });

            for (CameraNode cameraNode : cameras) {
                camerasPanel.add(createCameraFrame(cameraNode, layout.getColumns()));
            }

            updateWidths();
        }
        validate();
        repaint();
    }

    private void updateWidths() {
        for (CameraView cameraView : views) {
            int width = getWidth() / ((GridLayout) camerasPanel.getLayout()).getColumns()
                    - 2 * ((GridLayout) camerasPanel.getLayout()).getColumns() * MARGIN;
            cameraView.setWidth(width);
        }
    }

    private JPanel createCameraFrame(final CameraNode cameraNode, int nCols) {
        JPanel panel = new JPanel();
        BoxLayout boxLayout = new BoxLayout(panel, BoxLayout.Y_AXIS);
        panel.setLayout(boxLayout);

        JButton close = new JButton("x");
        close.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                synchronized (updateLock) {
                    cameras.remove(cameraNode);
                }
                MultiCameraViewTopComponent.this.update();
            }
        });
        close.setAlignmentX(Component.RIGHT_ALIGNMENT);
//        close.setBorderPainted(false);

//        JLabel jLabel = new JLabel();
        CameraView cameraView = new CameraView(cameraNode.getServer(), cameraNode.getCamera(), controller);
        cameraView.setScale(true);
        views.add(cameraView);

        panel.add(cameraView);
        JLabel name = new JLabel(cameraNode.getCamera().getId());
        name.setHorizontalAlignment(JLabel.CENTER);

        JPanel bottom = new JPanel();
        bottom.setLayout(new BorderLayout());
        bottom.add(name, BorderLayout.CENTER);
        bottom.add(close, BorderLayout.EAST);
        panel.add(bottom);
        cameraView.setAlignmentX(0.5f);
        name.setAlignmentX(0.5f);
        panel.setAlignmentX(0.5f);
        panel.setAlignmentY(0.5f);
        bottom.setSize(new Dimension(getWidth() / nCols - close.getPreferredSize().width - 2 * MARGIN, close.getPreferredSize().height));
        bottom.setMinimumSize(new Dimension(getWidth() / nCols - close.getPreferredSize().width - 2 * MARGIN, close.getPreferredSize().height));
        bottom.setMaximumSize(new Dimension(getWidth() / nCols - close.getPreferredSize().width - 2 * MARGIN, close.getPreferredSize().height));
        return panel;
    }

    public MultiCameraViewTopComponent() {
        initComponents();
        setName(NbBundle.getMessage(MultiCameraViewTopComponent.class, "CTL_MultiCameraViewTopComponent"));
        setToolTipText(NbBundle.getMessage(MultiCameraViewTopComponent.class, "HINT_MultiCameraViewTopComponent"));

        DropTarget dt = new DropTarget(this, this);
        dt.setDefaultActions(DnDConstants.ACTION_COPY);
        dt.setActive(true);

        controllerPanel.setLayout(new BoxLayout(controllerPanel, BoxLayout.PAGE_AXIS));
        controllerPanel.add(controller);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        nColsSpinner = new javax.swing.JSpinner();
        controllerPanel = new javax.swing.JPanel();
        jPanel1 = new javax.swing.JPanel();
        camerasScrollPane = new javax.swing.JScrollPane();
        camerasPanel = new javax.swing.JPanel();

        addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                formPropertyChange(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(MultiCameraViewTopComponent.class, "MultiCameraViewTopComponent.jLabel1.text")); // NOI18N

        nColsSpinner.setModel(new javax.swing.SpinnerNumberModel(3, 1, 8, 1));
        nColsSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                nColsSpinnerStateChanged(evt);
            }
        });

        javax.swing.GroupLayout controllerPanelLayout = new javax.swing.GroupLayout(controllerPanel);
        controllerPanel.setLayout(controllerPanelLayout);
        controllerPanelLayout.setHorizontalGroup(
            controllerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 658, Short.MAX_VALUE)
        );
        controllerPanelLayout.setVerticalGroup(
            controllerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 62, Short.MAX_VALUE)
        );

        camerasPanel.addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                camerasPanelComponentResized(evt);
            }
        });
        camerasPanel.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                camerasPanelPropertyChange(evt);
            }
        });
        camerasPanel.setLayout(new java.awt.GridLayout(1, 0));
        camerasScrollPane.setViewportView(camerasPanel);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 786, Short.MAX_VALUE)
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(camerasScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 786, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 408, Short.MAX_VALUE)
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(camerasScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 408, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(controllerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(nColsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 55, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jLabel1)
                        .addComponent(nColsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(controllerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void nColsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_nColsSpinnerStateChanged
        update();
        updateWidths();
    }//GEN-LAST:event_nColsSpinnerStateChanged

    private void formPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_formPropertyChange
    }//GEN-LAST:event_formPropertyChange
    int oldWidth = -1;

    @Override
    public void setSize(int width, int height) {
        super.setSize(width, height);
        if (camerasPanel.getWidth() != oldWidth) {
            oldWidth = camerasPanel.getWidth();
            updateWidths();
        }
    }

    @Override
    public void setSize(Dimension d) {
        super.setSize(d);
        if (camerasPanel.getWidth() != oldWidth) {
            oldWidth = camerasPanel.getWidth();
            updateWidths();
        }
    }

    private void camerasPanelPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_camerasPanelPropertyChange
    }//GEN-LAST:event_camerasPanelPropertyChange
    private long lastUpdate = System.currentTimeMillis();
    private void camerasPanelComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_camerasPanelComponentResized
        if (System.currentTimeMillis() - lastUpdate > 500) {
            MultiCameraViewTopComponent.this.update();
            lastUpdate = System.currentTimeMillis();
        }
    }//GEN-LAST:event_camerasPanelComponentResized
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel camerasPanel;
    private javax.swing.JScrollPane camerasScrollPane;
    private javax.swing.JPanel controllerPanel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JSpinner nColsSpinner;
    // End of variables declaration//GEN-END:variables

    @Override
    public void componentOpened() {
        // TODO add custom code on component opening
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }

    @Override
    public void dragEnter(DropTargetDragEvent dtde) {
    }

    @Override
    public void dragOver(DropTargetDragEvent dtde) {
    }

    @Override
    public void dropActionChanged(DropTargetDragEvent dtde) {
    }

    @Override
    public void dragExit(DropTargetEvent dte) {
    }

    @Override
    public void drop(DropTargetDropEvent dtde) {
        try {
            CameraNode cn = (CameraNode) dtde.getTransferable().getTransferData(CameraNode.DATA_FLAVOR);
            if (!cameras.contains(cn)) {
                cameras.add(cn);
            }
            update();
        } catch (UnsupportedFlavorException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }
}
